﻿package {
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	/**
	 * Caculate MD5 hash/digest.
	 * @param	input	Byte stream to hash, length not greater than uint.MAX_VALUE. Use ByteArray.writeUTFBytes() for strings.
	 * @return	MD5 hash.
	 */
	public function MD5(input:ByteArray):String {
		var bytes:ByteArray = new ByteArray();
		bytes.endian = Endian.LITTLE_ENDIAN;
		bytes.writeBytes(input);
		var len:uint = bytes.length;
		var padLen:uint = ((((len + 8) >>> 6) + 1) << 6) - 8;
		bytes.writeByte(0x80);
		for (var i:uint = len + 1; i < padLen; i++) bytes.writeByte(0);
		bytes.writeUnsignedInt(len << 3);
		bytes.writeUnsignedInt(len >>> 29);
		padLen += 8;
		bytes.position = 0;

		var a:uint = 0x67452301, b:uint = 0xefcdab89, c:uint = 0x98badcfe, d:uint = 0x10325476;
		var aa:uint, bb:uint, cc:uint, dd:uint;
		var x0:uint, x1:uint, x2:uint, x3:uint, x4:uint, x5:uint, x6:uint, x7:uint, x8:uint, x9:uint, x10:uint, x11:uint, x12:uint, x13:uint, x14:uint, x15:uint;
		for (i = 0; i < padLen; i += 64) {
			aa = a, bb = b, cc = c, dd = d;
			x0 = bytes.readUnsignedInt();
			x1 = bytes.readUnsignedInt();
			x2 = bytes.readUnsignedInt();
			x3 = bytes.readUnsignedInt();
			x4 = bytes.readUnsignedInt();
			x5 = bytes.readUnsignedInt();
			x6 = bytes.readUnsignedInt();
			x7 = bytes.readUnsignedInt();
			x8 = bytes.readUnsignedInt();
			x9 = bytes.readUnsignedInt();
			x10 = bytes.readUnsignedInt();
			x11 = bytes.readUnsignedInt();
			x12 = bytes.readUnsignedInt();
			x13 = bytes.readUnsignedInt();
			x14 = bytes.readUnsignedInt();
			x15 = bytes.readUnsignedInt();

			/* Round 1 */
			a += ((b & c) | ((~b) & d)) +  x0 + 0xd76aa478; a = (a <<  7) | (a >>> 25); a += b; /* 1 */
			d += ((a & b) | ((~a) & c)) +  x1 + 0xe8c7b756; d = (d << 12) | (d >>> 20); d += a; /* 2 */
			c += ((d & a) | ((~d) & b)) +  x2 + 0x242070db; c = (c << 17) | (c >>> 15); c += d; /* 3 */
			b += ((c & d) | ((~c) & a)) +  x3 + 0xc1bdceee; b = (b << 22) | (b >>> 10); b += c; /* 4 */
			a += ((b & c) | ((~b) & d)) +  x4 + 0xf57c0faf; a = (a <<  7) | (a >>> 25); a += b; /* 5 */
			d += ((a & b) | ((~a) & c)) +  x5 + 0x4787c62a; d = (d << 12) | (d >>> 20); d += a; /* 6 */
			c += ((d & a) | ((~d) & b)) +  x6 + 0xa8304613; c = (c << 17) | (c >>> 15); c += d; /* 7 */
			b += ((c & d) | ((~c) & a)) +  x7 + 0xfd469501; b = (b << 22) | (b >>> 10); b += c; /* 8 */
			a += ((b & c) | ((~b) & d)) +  x8 + 0x698098d8; a = (a <<  7) | (a >>> 25); a += b; /* 9 */
			d += ((a & b) | ((~a) & c)) +  x9 + 0x8b44f7af; d = (d << 12) | (d >>> 20); d += a; /* 10 */
			c += ((d & a) | ((~d) & b)) + x10 + 0xffff5bb1; c = (c << 17) | (c >>> 15); c += d; /* 11 */
			b += ((c & d) | ((~c) & a)) + x11 + 0x895cd7be; b = (b << 22) | (b >>> 10); b += c; /* 12 */
			a += ((b & c) | ((~b) & d)) + x12 + 0x6b901122; a = (a <<  7) | (a >>> 25); a += b; /* 13 */
			d += ((a & b) | ((~a) & c)) + x13 + 0xfd987193; d = (d << 12) | (d >>> 20); d += a; /* 14 */
			c += ((d & a) | ((~d) & b)) + x14 + 0xa679438e; c = (c << 17) | (c >>> 15); c += d; /* 15 */
			b += ((c & d) | ((~c) & a)) + x15 + 0x49b40821; b = (b << 22) | (b >>> 10); b += c; /* 16 */

			/* Round 2 */
			a += ((b & d) | (c & (~d))) +  x1 + 0xf61e2562; a = (a <<  5) | (a >>> 27); a += b; /* 17 */
			d += ((a & c) | (b & (~c))) +  x6 + 0xc040b340; d = (d <<  9) | (d >>> 23); d += a; /* 18 */
			c += ((d & b) | (a & (~b))) + x11 + 0x265e5a51; c = (c << 14) | (c >>> 18); c += d; /* 19 */
			b += ((c & a) | (d & (~a))) +  x0 + 0xe9b6c7aa; b = (b << 20) | (b >>> 12); b += c; /* 20 */
			a += ((b & d) | (c & (~d))) +  x5 + 0xd62f105d; a = (a <<  5) | (a >>> 27); a += b; /* 21 */
			d += ((a & c) | (b & (~c))) + x10 +  0x2441453; d = (d <<  9) | (d >>> 23); d += a; /* 22 */
			c += ((d & b) | (a & (~b))) + x15 + 0xd8a1e681; c = (c << 14) | (c >>> 18); c += d; /* 23 */
			b += ((c & a) | (d & (~a))) +  x4 + 0xe7d3fbc8; b = (b << 20) | (b >>> 12); b += c; /* 24 */
			a += ((b & d) | (c & (~d))) +  x9 + 0x21e1cde6; a = (a <<  5) | (a >>> 27); a += b; /* 25 */
			d += ((a & c) | (b & (~c))) + x14 + 0xc33707d6; d = (d <<  9) | (d >>> 23); d += a; /* 26 */
			c += ((d & b) | (a & (~b))) +  x3 + 0xf4d50d87; c = (c << 14) | (c >>> 18); c += d; /* 27 */
			b += ((c & a) | (d & (~a))) +  x8 + 0x455a14ed; b = (b << 20) | (b >>> 12); b += c; /* 28 */
			a += ((b & d) | (c & (~d))) + x13 + 0xa9e3e905; a = (a <<  5) | (a >>> 27); a += b; /* 29 */
			d += ((a & c) | (b & (~c))) +  x2 + 0xfcefa3f8; d = (d <<  9) | (d >>> 23); d += a; /* 30 */
			c += ((d & b) | (a & (~b))) +  x7 + 0x676f02d9; c = (c << 14) | (c >>> 18); c += d; /* 31 */
			b += ((c & a) | (d & (~a))) + x12 + 0x8d2a4c8a; b = (b << 20) | (b >>> 12); b += c; /* 32 */

			/* Round 3 */
			a += (b ^ c ^ d) +  x5 + 0xfffa3942; a = (a <<  4) | (a >>> 28); a += b; /* 33 */
			d += (a ^ b ^ c) +  x8 + 0x8771f681; d = (d << 11) | (d >>> 21); d += a; /* 34 */
			c += (d ^ a ^ b) + x11 + 0x6d9d6122; c = (c << 16) | (c >>> 16); c += d; /* 35 */
			b += (c ^ d ^ a) + x14 + 0xfde5380c; b = (b << 23) | (b >>>  9); b += c; /* 36 */
			a += (b ^ c ^ d) +  x1 + 0xa4beea44; a = (a <<  4) | (a >>> 28); a += b; /* 37 */
			d += (a ^ b ^ c) +  x4 + 0x4bdecfa9; d = (d << 11) | (d >>> 21); d += a; /* 38 */
			c += (d ^ a ^ b) +  x7 + 0xf6bb4b60; c = (c << 16) | (c >>> 16); c += d; /* 39 */
			b += (c ^ d ^ a) + x10 + 0xbebfbc70; b = (b << 23) | (b >>>  9); b += c; /* 40 */
			a += (b ^ c ^ d) + x13 + 0x289b7ec6; a = (a <<  4) | (a >>> 28); a += b; /* 41 */
			d += (a ^ b ^ c) +  x0 + 0xeaa127fa; d = (d << 11) | (d >>> 21); d += a; /* 42 */
			c += (d ^ a ^ b) +  x3 + 0xd4ef3085; c = (c << 16) | (c >>> 16); c += d; /* 43 */
			b += (c ^ d ^ a) +  x6 +  0x4881d05; b = (b << 23) | (b >>>  9); b += c; /* 44 */
			a += (b ^ c ^ d) +  x9 + 0xd9d4d039; a = (a <<  4) | (a >>> 28); a += b; /* 45 */
			d += (a ^ b ^ c) + x12 + 0xe6db99e5; d = (d << 11) | (d >>> 21); d += a; /* 46 */
			c += (d ^ a ^ b) + x15 + 0x1fa27cf8; c = (c << 16) | (c >>> 16); c += d; /* 47 */
			b += (c ^ d ^ a) +  x2 + 0xc4ac5665; b = (b << 23) | (b >>>  9); b += c; /* 48 */

			/* Round 4 */
			a += (c ^ (b | (~d))) +  x0 + 0xf4292244; a = (a <<  6) | (a >>> 26); a += b; /* 49 */
			d += (b ^ (a | (~c))) +  x7 + 0x432aff97; d = (d << 10) | (d >>> 22); d += a; /* 50 */
			c += (a ^ (d | (~b))) + x14 + 0xab9423a7; c = (c << 15) | (c >>> 17); c += d; /* 51 */
			b += (d ^ (c | (~a))) +  x5 + 0xfc93a039; b = (b << 21) | (b >>> 11); b += c; /* 52 */
			a += (c ^ (b | (~d))) + x12 + 0x655b59c3; a = (a <<  6) | (a >>> 26); a += b; /* 53 */
			d += (b ^ (a | (~c))) +  x3 + 0x8f0ccc92; d = (d << 10) | (d >>> 22); d += a; /* 54 */
			c += (a ^ (d | (~b))) + x10 + 0xffeff47d; c = (c << 15) | (c >>> 17); c += d; /* 55 */
			b += (d ^ (c | (~a))) +  x1 + 0x85845dd1; b = (b << 21) | (b >>> 11); b += c; /* 56 */
			a += (c ^ (b | (~d))) +  x8 + 0x6fa87e4f; a = (a <<  6) | (a >>> 26); a += b; /* 57 */
			d += (b ^ (a | (~c))) + x15 + 0xfe2ce6e0; d = (d << 10) | (d >>> 22); d += a; /* 58 */
			c += (a ^ (d | (~b))) +  x6 + 0xa3014314; c = (c << 15) | (c >>> 17); c += d; /* 59 */
			b += (d ^ (c | (~a))) + x13 + 0x4e0811a1; b = (b << 21) | (b >>> 11); b += c; /* 60 */
			a += (c ^ (b | (~d))) +  x4 + 0xf7537e82; a = (a <<  6) | (a >>> 26); a += b; /* 61 */
			d += (b ^ (a | (~c))) + x11 + 0xbd3af235; d = (d << 10) | (d >>> 22); d += a; /* 62 */
			c += (a ^ (d | (~b))) +  x2 + 0x2ad7d2bb; c = (c << 15) | (c >>> 17); c += d; /* 63 */
			b += (d ^ (c | (~a))) +  x9 + 0xeb86d391; b = (b << 21) | (b >>> 11); b += c; /* 64 */

			a += aa;
			b += bb;
			c += cc;
			d += dd;
		}

		var digest:ByteArray = new ByteArray();
		digest.endian = Endian.LITTLE_ENDIAN;
		digest.writeUnsignedInt(a);
		digest.writeUnsignedInt(b);
		digest.writeUnsignedInt(c);
		digest.writeUnsignedInt(d);
		var num:int, hex:String, str:String = "";
		for (i = 0; i < 16; i++ ) {
			num = digest[i];
			hex = num.toString(16);
			if (num < 0x10) hex = '0' + hex;
			str += hex;
		}
		return str;
	}
}
